/*
	Copyright (c) 2009, M.P. Bartnig & A. Gosman
	
	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:
	
	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.
	
	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/
package nl.sanity.net
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.events.EventDispatcher;
	import flash.media.Sound;
	import flash.text.StyleSheet;
	
	import nl.sanity.debug.LogConsole;
	import nl.sanity.events.QueueLoaderEvent;
	
	/**
	 * QueueLoader
	 * 
	 * @langversion ActionScript 3
	 *
	 * @author M.P. Bartnig
	 * @since  28/03/2009
	 * 
	 * TODO:  	- allow for simultaneous loading of items
	 * 			- calculation of total progress instead of only item progress
	 * 			- implementation of stopOnError property? 
	 */
	public class QueueLoader extends EventDispatcher
	{
		protected var _verbose						: Boolean;
		protected var _queuedItems					: Array;
		protected var _loadedItems					: Array;

		 
		// --- CONSTRUCTOR ----------------------------------------

		public function QueueLoader( verbose:Boolean = false ) 
		{	
			_verbose 		= verbose;
			
			if( _verbose ) LogConsole.log( "QueueLoader instantiated" );
			
			_queuedItems 			= new Array();
			_loadedItems			= new Array();
		}
		
		// --- PUBLIC FUNCTIONS ----------------------------------------
		
		public function add( url:String ):LoadItem
		{
			if( _verbose ) LogConsole.log( "QueueLoader.add: " + url );
			
			var item:LoadItem	= new LoadItem( url );
			addItem( item );
			
			return item;
		}
		
		public function addItem( item:LoadItem ):void
		{
			if( _verbose ) LogConsole.log( "QueueLoader.addItem | url: " + item.url );
			
			_queuedItems.push( item );
		}
		
		/**
		 * 	Starts loading the queue.
		 * 
		 * 	TODO: allow for simultaneous loading of items
		 * 
		 */
		public function start():void
		{
			if( _verbose ) LogConsole.log( "QueueLoader.start" );
			
			if ( _queuedItems.length > 0 )
			{
				// Notification
				dispatchLoadStart();
				loadNextItem();
			}
			else
			{
				// Notification
				dispatchLoadComplete();
			}
		}
		
		/**
		 * 	Tries to stop loading of the queue.
		 * 
		 * 	 TODO: call close() method on all items in the queue.
		 */ 
		public function stop():void
		{
			// TODO
		}
		
		public function pause():void
		{
			// TODO
		}
		
		public function getItem( id:String ):LoadItem
		{
			return getQueuedItem( id ) || getLoadedItem( id );
		}
		
		public function getQueuedItem( id:String ):LoadItem
		{
			for each( var queuedItem:LoadItem in _queuedItems )
			{
				if( queuedItem.id == id )
				{
					return queuedItem;
				}
			}
			
			return null;
		}
		
		public function getLoadedItem( id:String ):LoadItem
		{
			for each( var loadedItem:LoadItem in _loadedItems )
			{
				if( loadedItem.id == id )
				{
					return loadedItem;
				}
			}
			return null;
		}
		
		public function getItemContent( id:String ):*
		{
			if( _verbose ) LogConsole.log( "QueueLoader.getItemContent | id: " + id );
			
			var item:LoadItem 			= getLoadedItem( id );
			
			if( item )
			{
				return item.content;
			}
			
			return null;
		}
		
		public function getBitmap( id:String, smoothing:Boolean = true ):Bitmap
		{
			var content:*			= getItemContent( id );
			
			if ( !content || !content is Bitmap ) return null;
			
			var clonedBitmapData:BitmapData = Bitmap( content ).bitmapData.clone();
			var bmp:Bitmap					= new Bitmap( clonedBitmapData );
			bmp.smoothing					= smoothing;
			
			return bmp;
		}
		
		public function getMovieClip( id:String ):DisplayObject
		{ 
			var content:*			= getItemContent( id );
			
			if ( !content || !content is DisplayObject ) return null;
			
			return content as DisplayObject;
		}
		
		public function getSound( id:String ):Sound
		{
			var content:*			= getItemContent( id );
			
			if ( !content || !content is Sound ) return null;
			
			return content as Sound;
		}
		
		public function getXML( id:String ):XML
		{
			var content:*			= getItemContent( id );
			
			if ( !content || !content is XML ) return null;
			
			return content as XML;
		}
		
		public function getStyleSheet( id:String ):StyleSheet
		{
			var content:*			= getItemContent( id );
			
			if ( !content || !content is StyleSheet ) return null;
			
			return content as StyleSheet;
		}
		
		public function getText( id:String ):String
		{
			var content:* 			= getItemContent( id );
			
			if ( !content || !content is String ) return null;
			
			return content as String;
		}
		
		
		// --- PROTECTED FUNCTIONS ----------------------------------------
		
		protected function loadNextItem():void
		{
			// Check if all items in the queuelist are loaded
			if( _queuedItems.length == 0 )
			{
				// Notification
				dispatchLoadComplete();
			}
			else
			{
				loadItem( _queuedItems.shift() );
			}
		}
		
		protected function loadItem( item:LoadItem ):void
		{
			if( _verbose ) LogConsole.log( "QueueLoader.loadItem: " + item.id );
			
			// Notification
			dispatchItemLoadStart( item );
			
			item.addEventListener( QueueLoaderEvent.ON_ITEM_LOAD_PROGRESS, onItemLoadProgress );
			item.addEventListener( QueueLoaderEvent.ON_ITEM_LOAD_COMPLETE, onItemLoadComplete );
			item.addEventListener( QueueLoaderEvent.ON_ITEM_LOAD_ERROR, onItemLoadError );
			
			item.load();
		}
		
		// TODO: handle item progress
		protected function onItemLoadProgress( event:QueueLoaderEvent ):void
		{
			var item:LoadItem	= event.target as LoadItem;
			var pct:uint 		= 100 * event.data.bytesLoaded / event.data.bytesTotal;
			
			if( _verbose ) LogConsole.log( "QueueLoader.onItemLoadProgress | id: " + item.id + " pct: " + pct );
		}
		
		protected function onItemLoadComplete( event:QueueLoaderEvent ):void
		{
			var item:LoadItem 	= event.target as LoadItem;
			
			if( _verbose ) LogConsole.log( "QueueLoader.onItemLoadComplete: " + item.id );
			
			_loadedItems.push( item );
			
			// Notification
			dispatchItemLoadComplete( item );
			
			// Try to load the next item in the queue
			loadNextItem();
		}
		
		protected function onItemLoadError( event:QueueLoaderEvent ):void
		{
			var item:LoadItem 	= event.target as LoadItem;
			
			if( _verbose ) LogConsole.log( "QueueLoader.onItemLoadError: " + item.id );
			
			dispatchItemLoadError( item, event.error.toString() );
			
			// TODO: implement stopOnError property? 
			loadNextItem();
		}
		
		
		// DISPATCH HANDLERS
		
		protected function dispatchLoadStart():void
		{
			dispatchEvent( new QueueLoaderEvent( QueueLoaderEvent.ON_LOAD_START ) );
		}
		
		protected function dispatchLoadComplete():void
		{
			dispatchEvent( new QueueLoaderEvent( QueueLoaderEvent.ON_LOAD_COMPLETE ) );
		}
		
		protected function dispatchItemLoadStart( item:LoadItem ):void
		{
			dispatchEvent( new QueueLoaderEvent( QueueLoaderEvent.ON_ITEM_LOAD_START, { item: item } ) );
		}
		
		protected function dispatchItemLoadComplete( item:LoadItem ):void
		{
			dispatchEvent( new QueueLoaderEvent( QueueLoaderEvent.ON_ITEM_LOAD_COMPLETE, { item: item } ) );
		}
		
		protected function dispatchItemLoadError( item:LoadItem , error:String ):void
		{
			dispatchEvent( new QueueLoaderEvent( QueueLoaderEvent.ON_ITEM_LOAD_ERROR, { item: item, error: error } ) );
		}
		
		// --- GETTERS/SETTERS ----------------------------------------


	}
}